Figure 3

# set up the environment

library(Seurat)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)
library(reshape2)
library(CelltypeR)
Warning: replacing previous import ‘data.table::last’ by ‘dplyr::last’ when loading ‘CelltypeR’
Warning: replacing previous import ‘data.table::first’ by ‘dplyr::first’ when loading ‘CelltypeR’
Warning: replacing previous import ‘data.table::between’ by ‘dplyr::between’ when loading ‘CelltypeR’
Warning: replacing previous import ‘dplyr::filter’ by ‘flowCore::filter’ when loading ‘CelltypeR’
Warning: replacing previous import ‘ggplot2::margin’ by ‘randomForest::margin’ when loading ‘CelltypeR’
Warning: replacing previous import ‘dplyr::combine’ by ‘randomForest::combine’ when loading ‘CelltypeR’
Warning: replacing previous import ‘flowCore::filter’ by ‘dplyr::filter’ when loading ‘CelltypeR’
Warning: replacing previous import ‘flowViz::contour’ by ‘graphics::contour’ when loading ‘flowStats’

Attaching package: ‘CelltypeR’

The following object is masked from ‘package:ggplot2’:

    annotate

Figure 3A - predicted correlation matrix

hm <- heatmap(mat, 
        Rowv = NA,  # Don't cluster rows
        Colv = NA,  # Don't cluster columns
        col = colorRampPalette(c("white", "blue"))(100),  # Define a color palette
        main = "Reference Matrix")

hm
$rowInd
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13

$colInd
[1] 1 2 3 4 5 6 7 8 9

$Rowv
NULL

$Colv
NULL

Plot with ggplot Figure 3A


hm

pdf("/Users/rhalenathomas/Documents/Projects_Papers/PhenoID/ForFigures/June2023/ReferenceMatrix.pdf", width = 6.5, height = 5)
hm
dev.off()
quartz_off_screen 
                2 

Calculate correlations

Plot correlations Figure 3B, C and S3

p.cor2 <- plot_corr(cor2, threshold = 0.1, min_cells = 500)
Using X, best.cell.type, second.cell.type, cell.label, cell.label.ft, new_cell.label as id variables
Using X, best.cell.type, second.cell.type, cell.label, cell.label.ft, new_cell.label as id variables
Using X, best.cell.type, second.cell.type, cell.label, cell.label.ft, new_cell.label as id variables
pdf(paste(fig_outs,"CorPlots_thresh01.pdf",sep = ""))
p.cor2
[[1]]

[[2]]

[[3]]

[[4]]
Warning: Removed 1 rows containing missing values (`geom_violin()`).

[[5]]

[[6]]

[[7]]

[[8]]
dev.off()
null device 
          1 

Adjust pair of cell types plots to plot mixes with higher cell counts Figures S4,S5,S6

pdf(paste(fig_outs,"CAMcorpairs_thresh0553.pdf",sep=""))
p1
dev.off()
null device 
          1 
# Define the threshold number of pairs
df <- cor3 # threshold 0.35
threshold <- 150

# Filter the double-labeled cells
double.cells <- df[grep("-", df$cell.label),]
label_counts <- table(double.cells$cell.label)
filters_labels <- names(label_counts[label_counts >= threshold])
# Filter the double-labeled cells based on the filtered labels
filtered_double.cells <- double.cells[double.cells$cell.label %in% filters_labels, ]

# Melt the filtered double-labeled cells for plotting
df.melt.filtered <- melt(filtered_double.cells)
Using X, best.cell.type, second.cell.type, cell.label, cell.label.ft as id variables
# Plot the filtered double-labeled cells
p2 <- ggplot(df.melt.filtered, aes(x = variable, y = value, colour = variable, group = X))+
      geom_line(show.legend = FALSE, size = 0.1, color = "black") +
      geom_point() +
      scale_color_manual(values = c("#4E84C4", "#52854C","purple","orange")) +
      ylim(0.1, 0.8) +
      facet_wrap(~ as.factor(cell.label)) +
      ylab("Correlation Coefficient") +
      xlab("")

p2

pdf(paste(fig_outs,"CAMcorpairs_thresh35.pdf",sep=""))
p2
dev.off()
quartz_off_screen 
                2 

Make plots for Figure 3B and C


plot1b  
plot2
Warning: Removed 3 rows containing missing values (`geom_violin()`).

Figure 3 clustering

This is with the subsample of 9000 cells per hMO Figure 3D and E

Add different correlation assignments and visualize on UMAP from the low threshold for CAM

Visualize CAM with the signifance 0.553 threshold

seu <- AddMetaData(object=seu, metadata= cor1$cell.label, col.name = 'cor.labels05')

# see the labels added
unique(seu$cor.labels05)
 [1] "Unassigned"              "astrocytes"             
 [3] "neurons"                 "RG"                     
 [5] "OPC"                     "Epithelial"             
 [7] "NPC"                     "neurons-NPC"            
 [9] "oligodendrocyte"         "Endothelial"            
[11] "neurons-oligodendrocyte" "OPC-neurons"            
[13] "NPC-stemlike"            "oligodendrocyte-neurons"
[15] "neurons-OPC"             "RG-astrocytes"          
[17] "astrocytes-RG"           "NPC-neurons"            
[19] "oligodendrocyte-NPC"     "stemlike"               
[21] "NPC-oligodendrocyte"     "stemlike-NPC"           
[23] "Epithelial-NPC"          "RG-NPC"                 
[25] "OPC-oligodendrocyte"     "Endothelial-Epithelial" 
[27] "Epithelial-Endothelial"  "Epithelial-stemlike"    
[29] "RG-Epithelial"           "oligodendrocyte-OPC"    
[31] "astrocytes-stemlike"     "RG-oligodendrocyte"     
[33] "neurons-stemlike"        "Epithelial-RG"          
DimPlot(seu, group.by = 'cor.labels05', label = TRUE) + theme(legend.position = "none")

Visualize CAM assignments with threshold R = 0.35


seu <- AddMetaData(object=seu, metadata= cor3$cell.label, col.name = 'cor.labels035')

# see the labels added
#unique(seu$cor.labels01)

# plot the cluster predictions
#plot_lab_clust(seu, seu$RNA_snn_res.0.7, seu$cor.labels01)

DimPlot(seu, group.by = 'cor.labels035', label = TRUE) + theme(legend.position = "none")


# removing the combined cell labels that are low frequency will improve visualization
# Check if the cell type has a frequency less than 500 and assign new label accordingly

# Check if the cell type has a frequency less than 500 and assign new label accordingly
cor3$cell.label.ft <- ifelse(cor3$cell.label %in% freq.cor3.df$cell.label[freq.cor3.df$Freq < 200], "few", cor3$cell.label)


seu <- AddMetaData(object=seu, metadata= cor3$cell.label.ft, col.name = 'cor.labels035ft')
unique(seu$cor.labels035ft)
 [1] "astrocytes"              "RG"                     
 [3] "OPC"                     "neurons"                
 [5] "RG-Epithelial"           "neurons-oligodendrocyte"
 [7] "Endothelial"             "Epithelial-Endothelial" 
 [9] "oligodendrocyte-OPC"     "unassigned"             
[11] "Epithelial"              "NPC"                    
[13] "oligodendrocyte"         "oligodendrocyte-neurons"
[15] "few"                     "Epithelial-RG"          
[17] "neurons-OPC"             "stemlike"               
[19] "OPC-oligodendrocyte"     "RG-astrocytes"          
[21] "astrocytes-RG"           "Endothelial-Epithelial" 
[23] "OPC-neurons"            
DimPlot(seu, group.by = 'cor.labels035ft', label = TRUE, 
        label.size = 8, repel = TRUE) + theme(legend.position = "none")

NA
NA

Get the top assigned cells per cluster for each threshold

cor.ann.035 <- get_annotation(seu, seu.cluster = seu$RNA_snn_res.0.7, 
                          seu.label = seu$cor.labels035, top_n = 3, 
                          filter_out = c("Unknown","unknown","Mixed", 
                                         "unassigned","Unassigned"), 
                          Label = "CAM")
[1] "filtering"
cor.ann.01 <- get_annotation(seu, seu.cluster = seu$RNA_snn_res.0.7, 
                          seu.label = seu$cor.labels01, top_n = 3, 
                          filter_out = c("Unknown","unknown","Mixed","
                                         unassigned","Unassigned"), 
                          Label = "CAM")
[1] "filtering"
cor.ann.05 <- get_annotation(seu, seu.cluster = seu$RNA_snn_res.0.7, 
                          seu.label = seu$cor.labels05, top_n = 3, 
                          filter_out = c("Unknown","unknown","Mixed",
                                         "unassigned","Unassigned"), 
                          Label = "CAM")
[1] "filtering"

Visualize Marker expression

length(unique(seu$RNA_snn_res.0.7))
[1] 19
# 19
# if we want to plot by cluster we need a vector of from 0 to the n-1 clusters
cluster.num <- c(0:18)

plotmean(plot_type = 'heatmap',seu = seu, group = 'RNA_snn_res.0.7', markers = AB, 
                     var_names = cluster.num, slot = 'scale.data', xlab = "Cluster",
         ylab = "Antibody")

NA
NA

Feature plots

Idents(seu) <- "RNA_snn_res.0.7"

for (i in AB) {
  print(FeaturePlot(seu, features = i, min.cutoff = 'q1', max.cutoff = 'q97', label = TRUE))
}

NA
NA
pdf(paste(fig_outs,"HM9000_fig3E.pdf"),width = 8.5, height = 5)
DoHeatmap(seu, features = AB, size= 6,slot = "scale.data", group.colors = clust.colours, disp.max = 2, disp.min = -1.5,
          angle = 90) + scale_fill_gradientn(colors = c("#154c79", "#eeeee4", "#e28743")) + 
  theme(axis.text.y = element_text(size = 15))
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
dev.off()
null device 
          1 
seu <-readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/NatMethodJuneSubmission/Seu9000lablesJune23.RDS") 
Warning message:
R graphics engine version 15 is not supported by this version of RStudio. The Plots tab will be disabled until a newer version of RStudio is installed. 

Train a Random Forest classifier


markers <- rev(c("CD24","CD56","CD29","CD15","CD184","CD133","CD71","CD44","GLAST","AQP4","HepaCAM", "CD140a","O4"))
rf <- RFM_train(seurate_object = seu, 
                             AB_list = markers, annotations = seu$labels,
                      split = c(0.5,0.5),
                      downsample = "none",
                      seed = 222,
                      mytry = c(1:10),
                      maxnodes = c(10: 20),
                      trees = c(250, 500, 1000,2000),
                      start_node = 15)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBGaWd1cmUgMwoKYGBge3J9CiMgc2V0IHVwIHRoZSBlbnZpcm9ubWVudAoKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShDZWxsdHlwZVIpCgpgYGAKCkZpZ3VyZSAzQSAtIHByZWRpY3RlZCBjb3JyZWxhdGlvbiBtYXRyaXgKCgpgYGB7cn0KIyBGaWd1cmUgM0EgCiMgUGxvdCB0aGUgcmVmZXJlbmNlIG1hdHJpeCBmb3IgY29ycmVsYXRpb24KcmVmZXJlbmNlX3BhdGggPC0gIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0dJVEhVQi9DZWxsdHlwZVIvRXhhbXBsZU91dHMvRmluYWxSZWZlcmVuY2VNYXRyaXguY3N2IgpyZWZlcmVuY2VfZGF0YSA8LSByZWFkLmNzdihyZWZlcmVuY2VfcGF0aCkKIyB0aGUgcmVmZXJlbmNlIG1hdHJpeCBuZWVkIHRvIGJlIGluIHRoZSBmb3JtYXQgY2VsbCB0eXBlcyBhcyByb3dzIGFuZCBtYXJrZXJzIGFzIGNvbHVtbnMKIyB0aGVyZSBpcyBhIGNvbHVtbiBYIHdpdGggdGhlIG1hcmtlcnMKI2NvbG5hbWVzKHJlZmVyZW5jZV9kYXRhKQojIHdlIG5lZWQgYSByb3cgWCB3aXRoIHRoZSBjZWxsIHR5cGUgbmFtZXMKIyBUcmFuc3Bvc2UgdGhlIGRhdGFmcmFtZSBhbmQgY29udmVydCB2YWx1ZXMgdG8gbnVtZXJpYwoKcmVmZXJlbmNlX2RhdGEgPC0gcmJpbmQocmVmZXJlbmNlX2RhdGEsIGNvbG5hbWVzKHJlZmVyZW5jZV9kYXRhKSkKcm93bmFtZXMocmVmZXJlbmNlX2RhdGEpIDwtIHJlZmVyZW5jZV9kYXRhJFgKcmVmZXJlbmNlX2RhdGEgPC0gcmVmZXJlbmNlX2RhdGEgJT4lIHNlbGVjdCgtIlgiKQpkZl9yZWYgPC0gYXMuZGF0YS5mcmFtZSh0KHJlZmVyZW5jZV9kYXRhKSkKIyBtYWtlIGl0IG51bWVyaWMKZGZfcmVmMiA8LSBhcHBseShkZl9yZWYsIDIsIGFzLm51bWVyaWMpCmRmX3JlZjIgPC0gYXMuZGF0YS5mcmFtZShkZl9yZWYyKQojIGFkZCBiYWNrIHRoZSBjZWxsIHR5cGUgY29sdW1uCmRmX3JlZjIkWCA8LSBkZl9yZWYkWApjb2xuYW1lcyhkZl9yZWYyKQpyb3duYW1lcyhkZl9yZWYyKSA8LSBkZl9yZWYyJFgKaGVhZChkZl9yZWYyKQojIFBsb3QgdGhlIHJlZmVyZW5jZSBtYXRyaXgKcm93bmFtZXMocmVmZXJlbmNlX2RhdGEpIDwtIHJlZmVyZW5jZV9kYXRhJFgKZGZfcmVmMSA8LSByZWZlcmVuY2VfZGF0YSAlPiUgc2VsZWN0KC0iWCIpCm1hdCA8LSBhcy5tYXRyaXgoZGZfcmVmMSkKCmhtIDwtIGhlYXRtYXAobWF0LCAKICAgICAgICBSb3d2ID0gTkEsICAjIERvbid0IGNsdXN0ZXIgcm93cwogICAgICAgIENvbHYgPSBOQSwgICMgRG9uJ3QgY2x1c3RlciBjb2x1bW5zCiAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsICJibHVlIikpKDEwMCksICAjIERlZmluZSBhIGNvbG9yIHBhbGV0dGUKICAgICAgICBtYWluID0gIlJlZmVyZW5jZSBNYXRyaXgiKQoKaG0KCmBgYAoKUGxvdCB3aXRoIGdncGxvdCBGaWd1cmUgM0EKCmBgYHtyfQoKY29sbmFtZXMobWF0KSA8LSBjKCJBc3Ryb2N5dGUiLCJFbmRvdGhlbGlhbCIsIkVwaXRoZWxpYWwiLCJOZXVyb24iLAogICAgICAgICAgICAgICAgICAgIk5QQyIsIk9QQyIsIk9saWdvIiwiUkciLCJTdGVtIikKCm1hcmtlci5vcmRlciA8LSByZXYoYygiQ0QyNCIsIkNENTYiLCJDRDI5IiwiQ0QxNSIsIkNEMTg0IiwiQ0QxMzMiLCJDRDcxIiwiQ0Q0NCIsIkdMQVNUIiwiQVFQNCIsIkhlcGFDQU0iLCAiQ0QxNDBhIiwiTzQiKSkKIyByZWZvcm1hdCBkZgpsb25nX2RmIDwtIG1lbHQobWF0LCB2YXJuYW1lcyA9IGMoIk1hcmtlciIsICJDZWxsX1R5cGUiKSwgdmFsdWUubmFtZSA9ICJFeHByZXNzaW9uIikKbG9uZ19kZiRNYXJrZXIgPC0gZmFjdG9yKGxvbmdfZGYkTWFya2VyLCBsZXZlbHMgPSBtYXJrZXIub3JkZXIpCgpobSA8LSBnZ3Bsb3QobG9uZ19kZiwgYWVzKHggPSBDZWxsX1R5cGUsIHkgPSBNYXJrZXIsIGZpbGwgPSBFeHByZXNzaW9uKSkgKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpICsgICMgUmVtb3ZlIHRoZSBibGFjayBib3JkZXIgYnkgc2V0dGluZyBjb2xvciB0byAid2hpdGUiCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAic2Vhc2hlbGwyIiwgaGlnaCA9ICJyZWQzIikgKwogIGxhYnMoeCA9ICJDZWxsIFR5cGUiLCB5ID0gIk1hcmtlciIsIGZpbGwgPSAiUmVsYXRpdmUgRXhwcmVzc2lvbiIpICsgCiAgdGhlbWVfYncoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAgIyBSZW1vdmUgdGhlIHBhbmVsIGJvcmRlcgogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUsIHNpemUgPSAxMiwgY29sb3VyID0gImJsYWNrIiksIAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSAsdmp1c3QgPSAwLjUsIHNpemUgPSAxMiwgY29sb3VyID0gImJsYWNrIiksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkgICMgUmVtb3ZlIHRoZSBwYW5lbCBncmlkCgpobQoKcGRmKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvUHJvamVjdHNfUGFwZXJzL1BoZW5vSUQvRm9yRmlndXJlcy9KdW5lMjAyMy9SZWZlcmVuY2VNYXRyaXgucGRmIiwgd2lkdGggPSA2LjUsIGhlaWdodCA9IDUpCmhtCmRldi5vZmYoKQoKCmBgYAoKCgpDYWxjdWxhdGUgY29ycmVsYXRpb25zCgpgYGB7cn0KCnRlc3RfcGF0aCA8LSAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzLzlNQk8vcHJlcHJvX291dHNqYW4yMC05MDAwY2VsbHMvcHJlcHJvX291dHNyZXRyb3RyYW5zZm9ybWVkX2Zsb3dzZXQuY3N2IgoKdGVzdC5kZiA8LSByZWFkLmNzdih0ZXN0X3BhdGgpCmRpbSh0ZXN0LmRmKQpoZWFkKHRlc3QuZGYpCgojIHdpdGggMTMgYW50aWJvZGllcyBSIHZhbHVlIG9mIDAuNTUzIGhhcyBhIHNpZ25pZmljYW50IHAgdmFsdWUgbGVzcyB0aGFuIDAuMDUKY29yMSA8LSBmaW5kX2NvcnJlbGF0aW9uKHRlc3QgPSB0ZXN0LmRmLCByZWZlcmVuY2UgPSBkZl9yZWYyLAogICAgICAgICAgICAgICAgICAgICAgICBtaW5fY29yciA9IDAuNTUzLCBtaW5fZGlmZiA9IDAuMDUpCgp3cml0ZS5jc3YoY29yMSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy85TUJPL3ByZXByb19vdXRzamFuMjAtOTAwMGNlbGxzL2NvcnJlbGF0aW9ucy9jb3I5MDAwY2VsbHNSdGhyZXNoMDU1M19qdW5lMjAyM0IuY3N2IikKCmNvcjEgPC0gcmVhZC5jc3YoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy85TUJPL3ByZXByb19vdXRzamFuMjAtOTAwMGNlbGxzL2NvcnJlbGF0aW9ucy9jb3I5MDAwY2VsbHNSdGhyZXNoMDU1M19qdW5lMjAyM0IuY3N2IikKCgojIHNlZSBob3cgdGhlIGNlbGxzIG1vc3RseSB3b3VsZCBiZSBhc3NpZ25lZCBpZiBub3QgZm9yIHRoZSB0aHJlc2hvbGQKY29yMiA8LSBmaW5kX2NvcnJlbGF0aW9uKHRlc3QgPSB0ZXN0LmRmLCByZWZlcmVuY2UgPSBkZl9yZWYyLAogICAgICAgICAgICAgICAgICAgICAgICBtaW5fY29yciA9IDAuMSwgbWluX2RpZmYgPSAwLjA1KQoKd3JpdGUuY3N2KGNvcjIsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvOU1CTy9wcmVwcm9fb3V0c2phbjIwLTkwMDBjZWxscy9jb3JyZWxhdGlvbnMvY29yOTAwMGNlbGxzUnRocmVzaDAxX2p1bmUyMDIzLmNzdiIpCgpoZWFkKGNvcjIpCgpjb3IzIDwtIGZpbmRfY29ycmVsYXRpb24odGVzdCA9IHRlc3QuZGYsIHJlZmVyZW5jZSA9IGRmX3JlZjIsCiAgICAgICAgICAgICAgICAgICAgICAgIG1pbl9jb3JyID0gMC4zNSwgbWluX2RpZmYgPSAwLjA1KQoKd3JpdGUuY3N2KGNvcjMsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvOU1CTy9wcmVwcm9fb3V0c2phbjIwLTkwMDBjZWxscy9jb3JyZWxhdGlvbnMvY29yOTAwMGNlbGxzUnRocmVzaDAzNV9qdW5lMjAyMy5jc3YiKQoKCmBgYAoKUGxvdCBjb3JyZWxhdGlvbnMKRmlndXJlIDNCLCBDIGFuZCBTMwoKYGBge3J9CgojIHBsb3QgdGhlIG1haW4gZ3JvdXBzIC0gYW5kIHRoZSBjb3JyZWxhdGlvbiBjby1lZmZpY2llbnQgZm9yIHRoZSBhc3NpZ25lZCBncm91cAojIHBsb3RzIGFyZSBjcmVhdGVkIGJ5IHRoZSBwbG90X2NvcnIgZnVuY3Rpb24sIHdoaWNoIHVzZXMgZ2dwbG90MgpwbG90X2NvcnIoY29yMSwgdGhyZXNob2xkID0gMC41NTMsIG1pbl9jZWxscyA9IDcwKSAKcGxvdF9jb3JyKGNvcjIsIHRocmVzaG9sZCA9IDAuMSwgbWluX2NlbGxzID0gNTAwKQpwbG90X2NvcnIoY29yMywgdGhyZXNob2xkID0gMC4zNSwgbWluX2NlbGxzID0gNTAwKQoKIyBzYXZlIHRoZSBwbG90cwpmaWdfb3V0cyA8LSAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL1Byb2plY3RzX1BhcGVycy9QaGVub0lEL0ZvckZpZ3VyZXMvSnVuZTIwMjMvIgoKcC5jb3IxIDwtIHBsb3RfY29ycihjb3IxLCB0aHJlc2hvbGQgPSAwLjU1MywgbWluX2NlbGxzID0gNzApCgpwZGYocGFzdGUoZmlnX291dHMsIkNvclBsb3RzX3RocmVzaDA1NTMucGRmIixzZXAgPSAiIikpCnAuY29yMQpkZXYub2ZmKCkKCnAuY29yMiA8LSBwbG90X2NvcnIoY29yMiwgdGhyZXNob2xkID0gMC4xLCBtaW5fY2VsbHMgPSA1MDApCgoKCnBkZihwYXN0ZShmaWdfb3V0cywiQ29yUGxvdHNfdGhyZXNoMDEucGRmIixzZXAgPSAiIikpCnAuY29yMgpkZXYub2ZmKCkKI0hvdyBtYW55IGNlbGxzIGFyZSB0aGVyZSBpbiBlYWNoIGNhdGVnb3J5IC0gdGhpcyBpcyBpbiB0aGUgZnJlcXVlbmN5IHRhYmxlCmZyZXEuY29yMi5kZiA8LSBhcy5kYXRhLmZyYW1lKHAuY29yMltbMV1dKQoKcC5jb3IzIDwtIHBsb3RfY29ycihjb3IzLCB0aHJlc2hvbGQgPSAwLjM1LCBtaW5fY2VsbHMgPSA1MDApCgpwZGYocGFzdGUoZmlnX291dHMsIkNvclBsb3RzX3RocmVzaDAzNS5wZGYiLHNlcCA9ICIiKSkKcC5jb3IzCmRldi5vZmYoKQojSG93IG1hbnkgY2VsbHMgYXJlIHRoZXJlIGluIGVhY2ggY2F0ZWdvcnkgLSB0aGlzIGlzIGluIHRoZSBmcmVxdWVuY3kgdGFibGUKZnJlcS5jb3IzLmRmIDwtIGFzLmRhdGEuZnJhbWUocC5jb3IzW1sxXV0pCgoKCmBgYApBZGp1c3QgcGFpciBvZiBjZWxsIHR5cGVzIHBsb3RzIHRvIHBsb3QgbWl4ZXMgd2l0aCBoaWdoZXIgY2VsbCBjb3VudHMKRmlndXJlcyBTNCxTNSxTNiAKCmBgYHtyfQojIERlZmluZSB0aGUgdGhyZXNob2xkIG51bWJlciBvZiBwYWlycwpkZiA8LSBjb3IxICMgdGhyZXNob2xkIDAuNTUzCnRocmVzaG9sZCA8LSA4CgojIEZpbHRlciB0aGUgZG91YmxlLWxhYmVsZWQgY2VsbHMKZG91YmxlLmNlbGxzIDwtIGRmW2dyZXAoIi0iLCBkZiRjZWxsLmxhYmVsKSxdCmxhYmVsX2NvdW50cyA8LSB0YWJsZShkb3VibGUuY2VsbHMkY2VsbC5sYWJlbCkKZmlsdGVyc19sYWJlbHMgPC0gbmFtZXMobGFiZWxfY291bnRzW2xhYmVsX2NvdW50cyA+PSB0aHJlc2hvbGRdKQojIEZpbHRlciB0aGUgZG91YmxlLWxhYmVsZWQgY2VsbHMgYmFzZWQgb24gdGhlIGZpbHRlcmVkIGxhYmVscwpmaWx0ZXJlZF9kb3VibGUuY2VsbHMgPC0gZG91YmxlLmNlbGxzW2RvdWJsZS5jZWxscyRjZWxsLmxhYmVsICVpbiUgZmlsdGVyc19sYWJlbHMsIF0KCiMgTWVsdCB0aGUgZmlsdGVyZWQgZG91YmxlLWxhYmVsZWQgY2VsbHMgZm9yIHBsb3R0aW5nCmRmLm1lbHQuZmlsdGVyZWQgPC0gbWVsdChmaWx0ZXJlZF9kb3VibGUuY2VsbHMpCgojIFBsb3QgdGhlIGZpbHRlcmVkIGRvdWJsZS1sYWJlbGVkIGNlbGxzCnAxIDwtIGdncGxvdChkZi5tZWx0LmZpbHRlcmVkLCBhZXMoeCA9IHZhcmlhYmxlLCB5ID0gdmFsdWUsIGNvbG91ciA9IHZhcmlhYmxlLCBncm91cCA9IFgpKSsKICAgICAgZ2VvbV9saW5lKHNob3cubGVnZW5kID0gRkFMU0UsIHNpemUgPSAwLjEsIGNvbG9yID0gImJsYWNrIikgKwogICAgICBnZW9tX3BvaW50KCkgKwogICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzRFODRDNCIsICIjNTI4NTRDIiwicHVycGxlIiwib3JhbmdlIikpICsKICAgICAgeWxpbSgwLCAxKSArCiAgICAgIGZhY2V0X3dyYXAofiBhcy5mYWN0b3IoY2VsbC5sYWJlbCkpICsKICAgICAgeWxhYigiQ29ycmVsYXRpb24gQ29lZmZpY2llbnQiKSArCiAgICAgIHhsYWIoIiIpCgpwMQoKcGRmKHBhc3RlKGZpZ19vdXRzLCJDQU1jb3JwYWlyc190aHJlc2gwNTUzLnBkZiIsc2VwPSIiKSkKcDEKZGV2Lm9mZigpCgpgYGAKCgpgYGB7cn0KIyBEZWZpbmUgdGhlIHRocmVzaG9sZCBudW1iZXIgb2YgcGFpcnMKZGYgPC0gY29yMyAjIHRocmVzaG9sZCAwLjM1CnRocmVzaG9sZCA8LSAxNTAKCiMgRmlsdGVyIHRoZSBkb3VibGUtbGFiZWxlZCBjZWxscwpkb3VibGUuY2VsbHMgPC0gZGZbZ3JlcCgiLSIsIGRmJGNlbGwubGFiZWwpLF0KbGFiZWxfY291bnRzIDwtIHRhYmxlKGRvdWJsZS5jZWxscyRjZWxsLmxhYmVsKQpmaWx0ZXJzX2xhYmVscyA8LSBuYW1lcyhsYWJlbF9jb3VudHNbbGFiZWxfY291bnRzID49IHRocmVzaG9sZF0pCiMgRmlsdGVyIHRoZSBkb3VibGUtbGFiZWxlZCBjZWxscyBiYXNlZCBvbiB0aGUgZmlsdGVyZWQgbGFiZWxzCmZpbHRlcmVkX2RvdWJsZS5jZWxscyA8LSBkb3VibGUuY2VsbHNbZG91YmxlLmNlbGxzJGNlbGwubGFiZWwgJWluJSBmaWx0ZXJzX2xhYmVscywgXQoKIyBNZWx0IHRoZSBmaWx0ZXJlZCBkb3VibGUtbGFiZWxlZCBjZWxscyBmb3IgcGxvdHRpbmcKZGYubWVsdC5maWx0ZXJlZCA8LSBtZWx0KGZpbHRlcmVkX2RvdWJsZS5jZWxscykKCiMgUGxvdCB0aGUgZmlsdGVyZWQgZG91YmxlLWxhYmVsZWQgY2VsbHMKcDIgPC0gZ2dwbG90KGRmLm1lbHQuZmlsdGVyZWQsIGFlcyh4ID0gdmFyaWFibGUsIHkgPSB2YWx1ZSwgY29sb3VyID0gdmFyaWFibGUsIGdyb3VwID0gWCkpKwogICAgICBnZW9tX2xpbmUoc2hvdy5sZWdlbmQgPSBGQUxTRSwgc2l6ZSA9IDAuMSwgY29sb3IgPSAiYmxhY2siKSArCiAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjNEU4NEM0IiwgIiM1Mjg1NEMiLCJwdXJwbGUiLCJvcmFuZ2UiKSkgKwogICAgICB5bGltKDAuMSwgMC44KSArCiAgICAgIGZhY2V0X3dyYXAofiBhcy5mYWN0b3IoY2VsbC5sYWJlbCkpICsKICAgICAgeWxhYigiQ29ycmVsYXRpb24gQ29lZmZpY2llbnQiKSArCiAgICAgIHhsYWIoIiIpCgpwMgoKcGRmKHBhc3RlKGZpZ19vdXRzLCJDQU1jb3JwYWlyc190aHJlc2gzNS5wZGYiLHNlcD0iIikpCnAyCmRldi5vZmYoKQoKYGBgCgoKCk1ha2UgcGxvdHMgZm9yIEZpZ3VyZSAzQiBhbmQgQwoKYGBge3J9CgojIHBsb3R0aW5nIHNjcmlwdCBmcm9tIGluc2lkZSB0aGUgZnVuY3Rpb24gYWJvdmUKdGhyZXNob2xkID0gMC41NTMKbWluX2NlbGxzID0gNTUKCiMgdmlvbGluIHBsb3QKZGYuZmlsdGVyIDwtIGNvcjEgJT4lIGdyb3VwX2J5KGNlbGwubGFiZWwpICU+JSBkcGx5cjo6ZmlsdGVyKG4oKT4gbWluX2NlbGxzKQoKcGxvdDIgPC0gZ2dwbG90KGRmLmZpbHRlciwgYWVzKHggPSBiZXN0LmNlbGwudHlwZSwgeSA9IGNvci4xLCBmaWxsID0gYmVzdC5jZWxsLnR5cGUpKSArCiAgICBnZW9tX3Zpb2xpbih0cmltID0gRkFMU0UpICsKICAgIHlsaW0oMCwgMSkgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEyKSkgKwogICAgeWxhYigiY29ycmVsYXRpb24gY29lZmZpY2llbnQiKSArCiAgICB4bGFiKCJDZWxsIHR5cGUgd2l0aCBtYXggY29ycmVsYXRpb24gY29lZmZpY2llbnQiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0aHJlc2hvbGQpICsKICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkNlbGwgVHlwZSIpKQoKCiMgc2F2ZQpwZGYocGFzdGUoZmlnX291dHMsIlZsbkNBTXRocmVzaDUzMy5wZGYiLHNlcD0iIiksd2lkdGggPSA1LGhlaWdodCA9IDQpCnBsb3QyCmRldi5vZmYoKQojIGJhciBjaGFydCB3aXRoIHRoZSBSID4gMC41NTMgdGhyZXNob2xkCgpkZi5maWx0ZXIyIDwtIGNvcjEgJT4lCiAgICBmaWx0ZXIoIWNlbGwubGFiZWwgJWluJSBjKCJVbmFzc2lnbmVkIiwgInVuYXNzaWduZWQiKSkgJT4lCiAgICBncm91cF9ieShjZWxsLmxhYmVsKSAlPiUKICAgIGZpbHRlcihuKCkgPiBtaW5fY2VsbHMpCiAgCgogIHBsb3QxYiA8LQogICAgZ2dwbG90KGRmLmZpbHRlcjIsIGFlcyh4ID0gcmVvcmRlcigKICAgICAgY2VsbC5sYWJlbCwgY2VsbC5sYWJlbCwgZnVuY3Rpb24oeCkgLSBsZW5ndGgoeCkpLAogICAgICBmaWxsID0gY2VsbC5sYWJlbCkpICsgZ2VvbV9iYXIoKSArIHRoZW1lX2NsYXNzaWMoKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG91ciA9ICJibGFjayIsIGFuZ2xlID0gOTAsIGhqdXN0PTAuOTksdmp1c3Q9MC41KSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3VyID0gImJsYWNrIiksCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvdXIgPSAiYmxhY2siKSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG91ciA9ICJibGFjayIpLAogICAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMTUsIDEsIDEsIDEpKSArCgogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsgIyB0YWtlIHRoZSBzcGFjZSBhd2F5IGJlbG93IHRoZSBiYXJzCiAgICB4bGFiKCdBc3NpZ25lZCBjZWxsIHR5cGUnKSArCiAgICB5bGFiKCdudW1iZXIgb2YgY2VsbCcpICsKICAgIGxhYnMoZmlsbD0nQ2VsbCBUeXBlcycpCiAgCgpwZGYocGFzdGUoZmlnX291dHMsIkJhcmNoYXJ0Q0FNdGhyZXNoNTMzLnBkZiIsc2VwPSIiKSx3aWR0aCA9IDUsaGVpZ2h0ID0gNCkKcGxvdDFiCmRldi5vZmYoKQoKcGxvdDFiICAKcGxvdDIKCmBgYAoKCgoKCgpGaWd1cmUgMyBjbHVzdGVyaW5nCgpUaGlzIGlzIHdpdGggdGhlIHN1YnNhbXBsZSBvZiA5MDAwIGNlbGxzIHBlciBoTU8KRmlndXJlIDNEIGFuZCBFCgpgYGB7cn0KIyByZWFkIGluIHRoZSBzZXVyYXQgb2JqZWN0cwpzZXUgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzLzlNQk8vcHJlcHJvX291dHNqYW4yMC05MDAwY2VsbHMvRmlndXJlMy9jbHVzdGVyX3BhcmFtZXRlcnMvcmV0cm8tbG91di1tb3JlcGFybS9yZXRyb3RyYW5zTG91dmFpblNldXJhdE9iamVjdDYwLlJkcyIpCkFCIDwtIGMoIkNEMjQiLCJDRDU2IiwiQ0Q3MSIsIkNEMjkiLCJDRDE1IiwgIkNEMTg0IiwiQ0QxMzMiLCAiR0xBU1QiLCJDRDQ0IiwiQVFQNCIsIkhlcGFDQU0iLCJDRDE0MGEiLCAiTzQiICkKCgojIHRoZSBoaWdoZXN0IFJhbmQgSW5kZXggYXJlIHJlcyA9IDAuMSwgNyBjbHVzdGVycywgcmVzID0gMC4xNSwgOSBjbHVzdGVycy4gIEJvdGggdmVyeSBsb3cgUkkgc3RkLgojIHRoZSBoaWdoIFJJIHdpdGggbG93IHN0ZCBpcyAwLjMgYW5kIDAuNywgY2x1c3RlciBudW1iZXJzIGFsc28gaGF2ZSBsb3cgc3RkIAojIGZyb20gYm9vdHN0cmFwIDEwMFgKIyBhbm5vdGF0aW9uIGlzIGVhc2llciB3aXRoIDE4LTI1IGNsdXN0ZXJzIAoKIyBzZXUgPC0gUnVuVU1BUChzZXUsc3ByZWFkID0gMSwgbWluLmRpc3QgPSAwLjA1LCBkaW1zID0gMToxMikKCkRpbVBsb3Qoc2V1LCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC43JywgcmVwZWwgPSBUUlVFKQoKCgpgYGAKCkFkZCBkaWZmZXJlbnQgY29ycmVsYXRpb24gYXNzaWdubWVudHMgYW5kIHZpc3VhbGl6ZSBvbiBVTUFQIGZyb20gdGhlIGxvdyB0aHJlc2hvbGQgZm9yIENBTQoKYGBge3IsIGZpZy53aWR0aD01fQoKc2V1IDwtIEFkZE1ldGFEYXRhKG9iamVjdD1zZXUsIG1ldGFkYXRhPSBjb3IyJGNlbGwubGFiZWwsIGNvbC5uYW1lID0gJ2Nvci5sYWJlbHMwMScpCgojIHNlZSB0aGUgbGFiZWxzIGFkZGVkCiN1bmlxdWUoc2V1JGNvci5sYWJlbHMwMSkKCiMgcGxvdCB0aGUgY2x1c3RlciBwcmVkaWN0aW9ucwojcGxvdF9sYWJfY2x1c3Qoc2V1LCBzZXUkUk5BX3Nubl9yZXMuMC43LCBzZXUkY29yLmxhYmVsczAxKQoKRGltUGxvdChzZXUsIGdyb3VwLmJ5ID0gJ2Nvci5sYWJlbHMwMScsIGxhYmVsID0gVFJVRSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIHJlbW92aW5nIHRoZSBjb21iaW5lZCBjZWxsIGxhYmVscyB0aGF0IGFyZSBsb3cgZnJlcXVlbmN5IHdpbGwgaW1wcm92ZSB2aXN1YWxpemF0aW9uCiMgQ2hlY2sgaWYgdGhlIGNlbGwgdHlwZSBoYXMgYSBmcmVxdWVuY3kgbGVzcyB0aGFuIDUwMCBhbmQgYXNzaWduIG5ldyBsYWJlbCBhY2NvcmRpbmdseQoKIyBDaGVjayBpZiB0aGUgY2VsbCB0eXBlIGhhcyBhIGZyZXF1ZW5jeSBsZXNzIHRoYW4gNTAwIGFuZCBhc3NpZ24gbmV3IGxhYmVsIGFjY29yZGluZ2x5CmNvcjIkY2VsbC5sYWJlbC5mdCA8LSBpZmVsc2UoY29yMiRjZWxsLmxhYmVsICVpbiUgZnJlcS5jb3IyLmRmJGNlbGwubGFiZWxbZnJlcS5jb3IyLmRmJEZyZXEgPCA1MDBdLCAiZmV3IiwgY29yMiRjZWxsLmxhYmVsKQoKCnNldSA8LSBBZGRNZXRhRGF0YShvYmplY3Q9c2V1LCBtZXRhZGF0YT0gY29yMiRjZWxsLmxhYmVsLmZ0LCBjb2wubmFtZSA9ICdjb3IubGFiZWxzMDFmdCcpCnVuaXF1ZShzZXUkY29yLmxhYmVsczAxZnQpCnBsb3RfbGFiX2NsdXN0KHNldSwgc2V1JFJOQV9zbm5fcmVzLjAuNywgc2V1JGNvci5sYWJlbHMwMWZ0KQoKRGltUGxvdChzZXUsIGdyb3VwLmJ5ID0gJ2Nvci5sYWJlbHMwMWZ0JywgbGFiZWwgPSBUUlVFLCAKICAgICAgICBsYWJlbC5zaXplID0gOCwgcmVwZWwgPSBUUlVFKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgpgYGAKVmlzdWFsaXplIENBTSB3aXRoIHRoZSBzaWduaWZhbmNlIDAuNTUzIHRocmVzaG9sZAoKYGBge3IsIGZpZy53aWR0aD01fQoKc2V1IDwtIEFkZE1ldGFEYXRhKG9iamVjdD1zZXUsIG1ldGFkYXRhPSBjb3IxJGNlbGwubGFiZWwsIGNvbC5uYW1lID0gJ2Nvci5sYWJlbHMwNScpCgojIHNlZSB0aGUgbGFiZWxzIGFkZGVkCnVuaXF1ZShzZXUkY29yLmxhYmVsczA1KQoKCkRpbVBsb3Qoc2V1LCBncm91cC5ieSA9ICdjb3IubGFiZWxzMDUnLCBsYWJlbCA9IFRSVUUpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCmBgYAoKVmlzdWFsaXplIENBTSBhc3NpZ25tZW50cyB3aXRoIHRocmVzaG9sZCBSID0gMC4zNQoKYGBge3IsIGZpZy53aWR0aD01fQoKc2V1IDwtIEFkZE1ldGFEYXRhKG9iamVjdD1zZXUsIG1ldGFkYXRhPSBjb3IzJGNlbGwubGFiZWwsIGNvbC5uYW1lID0gJ2Nvci5sYWJlbHMwMzUnKQoKIyBzZWUgdGhlIGxhYmVscyBhZGRlZAojdW5pcXVlKHNldSRjb3IubGFiZWxzMDEpCgojIHBsb3QgdGhlIGNsdXN0ZXIgcHJlZGljdGlvbnMKI3Bsb3RfbGFiX2NsdXN0KHNldSwgc2V1JFJOQV9zbm5fcmVzLjAuNywgc2V1JGNvci5sYWJlbHMwMSkKCkRpbVBsb3Qoc2V1LCBncm91cC5ieSA9ICdjb3IubGFiZWxzMDM1JywgbGFiZWwgPSBUUlVFKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgcmVtb3ZpbmcgdGhlIGNvbWJpbmVkIGNlbGwgbGFiZWxzIHRoYXQgYXJlIGxvdyBmcmVxdWVuY3kgd2lsbCBpbXByb3ZlIHZpc3VhbGl6YXRpb24KIyBDaGVjayBpZiB0aGUgY2VsbCB0eXBlIGhhcyBhIGZyZXF1ZW5jeSBsZXNzIHRoYW4gNTAwIGFuZCBhc3NpZ24gbmV3IGxhYmVsIGFjY29yZGluZ2x5CgojIENoZWNrIGlmIHRoZSBjZWxsIHR5cGUgaGFzIGEgZnJlcXVlbmN5IGxlc3MgdGhhbiA1MDAgYW5kIGFzc2lnbiBuZXcgbGFiZWwgYWNjb3JkaW5nbHkKY29yMyRjZWxsLmxhYmVsLmZ0IDwtIGlmZWxzZShjb3IzJGNlbGwubGFiZWwgJWluJSBmcmVxLmNvcjMuZGYkY2VsbC5sYWJlbFtmcmVxLmNvcjMuZGYkRnJlcSA8IDIwMF0sICJmZXciLCBjb3IzJGNlbGwubGFiZWwpCgoKc2V1IDwtIEFkZE1ldGFEYXRhKG9iamVjdD1zZXUsIG1ldGFkYXRhPSBjb3IzJGNlbGwubGFiZWwuZnQsIGNvbC5uYW1lID0gJ2Nvci5sYWJlbHMwMzVmdCcpCnVuaXF1ZShzZXUkY29yLmxhYmVsczAzNWZ0KQoKRGltUGxvdChzZXUsIGdyb3VwLmJ5ID0gJ2Nvci5sYWJlbHMwMzVmdCcsIGxhYmVsID0gVFJVRSwgCiAgICAgICAgbGFiZWwuc2l6ZSA9IDgsIHJlcGVsID0gVFJVRSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKYGBgCgpHZXQgdGhlIHRvcCBhc3NpZ25lZCBjZWxscyBwZXIgY2x1c3RlciBmb3IgZWFjaCB0aHJlc2hvbGQKCmBgYHtyfQpjb3IuYW5uLjAzNSA8LSBnZXRfYW5ub3RhdGlvbihzZXUsIHNldS5jbHVzdGVyID0gc2V1JFJOQV9zbm5fcmVzLjAuNywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2V1LmxhYmVsID0gc2V1JGNvci5sYWJlbHMwMzUsIHRvcF9uID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyX291dCA9IGMoIlVua25vd24iLCJ1bmtub3duIiwiTWl4ZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5hc3NpZ25lZCIsIlVuYXNzaWduZWQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgTGFiZWwgPSAiQ0FNIikKY29yLmFubi4wMSA8LSBnZXRfYW5ub3RhdGlvbihzZXUsIHNldS5jbHVzdGVyID0gc2V1JFJOQV9zbm5fcmVzLjAuNywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2V1LmxhYmVsID0gc2V1JGNvci5sYWJlbHMwMSwgdG9wX24gPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJfb3V0ID0gYygiVW5rbm93biIsInVua25vd24iLCJNaXhlZCIsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuYXNzaWduZWQiLCJVbmFzc2lnbmVkIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgIExhYmVsID0gIkNBTSIpCmNvci5hbm4uMDUgPC0gZ2V0X2Fubm90YXRpb24oc2V1LCBzZXUuY2x1c3RlciA9IHNldSRSTkFfc25uX3Jlcy4wLjcsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHNldS5sYWJlbCA9IHNldSRjb3IubGFiZWxzMDUsIHRvcF9uID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyX291dCA9IGMoIlVua25vd24iLCJ1bmtub3duIiwiTWl4ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bmFzc2lnbmVkIiwiVW5hc3NpZ25lZCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBMYWJlbCA9ICJDQU0iKQoKCmBgYAoKVmlzdWFsaXplIE1hcmtlciBleHByZXNzaW9uCgpgYGB7cn0KbGVuZ3RoKHVuaXF1ZShzZXUkUk5BX3Nubl9yZXMuMC43KSkKIyAxOQojIGlmIHdlIHdhbnQgdG8gcGxvdCBieSBjbHVzdGVyIHdlIG5lZWQgYSB2ZWN0b3Igb2YgZnJvbSAwIHRvIHRoZSBuLTEgY2x1c3RlcnMKY2x1c3Rlci5udW0gPC0gYygwOjE4KQoKcGxvdG1lYW4ocGxvdF90eXBlID0gJ2hlYXRtYXAnLHNldSA9IHNldSwgZ3JvdXAgPSAnUk5BX3Nubl9yZXMuMC43JywgbWFya2VycyA9IEFCLCAKICAgICAgICAgICAgICAgICAgICAgdmFyX25hbWVzID0gY2x1c3Rlci5udW0sIHNsb3QgPSAnc2NhbGUuZGF0YScsIHhsYWIgPSAiQ2x1c3RlciIsCiAgICAgICAgIHlsYWIgPSAiQW50aWJvZHkiKQoKCmBgYApGZWF0dXJlIHBsb3RzCgpgYGB7cn0KSWRlbnRzKHNldSkgPC0gIlJOQV9zbm5fcmVzLjAuNyIKCmZvciAoaSBpbiBBQikgewogIHByaW50KEZlYXR1cmVQbG90KHNldSwgZmVhdHVyZXMgPSBpLCBtaW4uY3V0b2ZmID0gJ3ExJywgbWF4LmN1dG9mZiA9ICdxOTcnLCBsYWJlbCA9IFRSVUUpKQp9CgoKYGBgCgoKCgpgYGB7cn0KCgojIGFubm90YXRlIGNlbGxzIApJZGVudHMoc2V1KSA8LSAiUk5BX3Nubl9yZXMuMC43IgpjbHVzdGVyLmlkcyA8LSBjKCJVbmFzc2lnbmVkIiwiR2xpYWwtbGluZWFnZSIsIk5ldXJvbnMgMSIsIlJhZGlhbCBHbGlhIDEiLAogICAgICAgICAgICAiUmFkaWFsIEdsaWEgMyIsIkVwaXRoZWxpYWwiLCJOZXVyb25zIDIiLAogICAgICAgICAgICAiQXN0cm9jeXRlcyAxIiwiQXN0cm9jeXRlcyAxIiwKICAgICAgICAgICAgICAgICAiQXN0cm9jeXRlcyAyIiwiTmV1cm9ucyAyIiwiTlBDIiwiUmFkaWFsIEdsaWEgMSIsCiAgICAgICAgICAgICJSYWRpYWwgR2xpYSAyIiwKICAgICAgICAgICAgICAgICAiRW5kb3RoZWxpYWwiLCJPbGlnb2RlbmRyb2N5dGVzIiwiT1BDIiwiU3RlbSBjZWxsIGxpa2UiLAogICAgICAgICAgICAgICAgICJOUEMiKQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUpCnNldSA8LSBSZW5hbWVJZGVudHMoc2V1LCBjbHVzdGVyLmlkcykKc2V1JGxhYmVscyA8LSBJZGVudHMoc2V1KQoKRGltUGxvdChzZXUsIGxhYmVsID0gVFJVRSkKIyB3aXRoIG1vcmUgc3ViZ3JvdXBzCklkZW50cyhzZXUpIDwtICJsYWJlbHMiCmxldmVscyhzZXUpCgojIHRoZXJlIGFyZSAxNiBsZXZlbHMgaW4gdGhlIGNlbGwgdHlwZSBhbm5vdGF0aW9ucyB3aXRoIG1ham9yIGdyb3VwcwoKIyBjaGFuZ2UgdGhlIG9yZGVyIG9mIHRoZSBjZWxsIHR5cGVzIG9uIHRoZSBsZWdlbmQgb2YgdGhlIHVtYXAKY2VsbC50eXBlLm9yZGVyIDwtIGMoIkFzdHJvY3l0ZXMgMSIsICJBc3Ryb2N5dGVzIDIiLCJSYWRpYWwgR2xpYSAxIiwiUmFkaWFsIEdsaWEgMiIsCiAgICAgICAgICAgICAgICAgICAgICJSYWRpYWwgR2xpYSAzIiwiR2xpYWwtbGluZWFnZSIsCiAgICAgICAgICAgICAgICAgICAgICJFcGl0aGVsaWFsIiwiRW5kb3RoZWxpYWwiLAogICAgICAgICAgICAgICAgICAgICAiTlBDIiwiTmV1cm9ucyAxIiwiTmV1cm9ucyAyIiwKICAgICAgICAgICAgICAgICAgICAgIk9saWdvZGVuZHJvY3l0ZXMiLCJPUEMiLCJTdGVtIGNlbGwgbGlrZSIsIlVuYXNzaWduZWQiKQpjZWxsLnR5cGUub3JkZXIgPC0gcmV2KGNlbGwudHlwZS5vcmRlcikKCiMgY29sb3VyIG9yZGVyIHRvIG1hdGNoIGNlbGwgdHlwZSBvcmRlcgpjbHVzdC5jb2xvdXJzIDwtIGMoImNob2NvbGF0ZTIiLCJkYXJrb3JhbmdlIiwicGluayIsImNvcmFsMSIsImxpZ2h0cGluazMiLAogICAgICAgICAgICAgICAgICAgIm1pc3R5cm9zZTIiLAogICAgICAgICAgICAgICAgICAgInN0ZWVsYmx1ZTMiLCJkZWVwc2t5Ymx1ZSIsCiAgICAgICAgICAgICAgICAgICAibWVkaXVtcHVycGxlMSIsInB1cnBsZSIsInBsdW0zIiwKICAgICAgICAgICAgICAgICAgICJzZWFncmVlbjMiLCJvbGl2ZWRyYWI0IiwidG9tYXRvMyIsImJ1cmx5d29vZDMiKQogICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAKICMgICAgICAgICAgICAgICAgICAicGx1bTEiLCJwdXJwbGUiLCJtYWdlbnRhMyIsIm1lZGl1bXB1cnBsZTEiLCJkYXJrb3JjaGlkIiwicGx1bTMiLAojICAgICAgICAgICAgICJzdGVlbGJsdWUzIiwiZGFya29yYW5nZTEiLCJvcmFuZ2UxIiwibGlnaHRjb3JhbCIsImNvcmFsMSIsIm9yYW5nZXJlZDEiLCJsaWdodHNhbG1vbiIsCiAjICAgICAgICAgICAgImN5YW4iKQoKIyBGaWd1cmUgM0QgVU1BUCB3aXRoIGFubm90YXRlZCBjbHVzdGVycwojIHVzZSBQREYgZm9yIGZpZ3VyZSBmb3IgY29ycmVjdCByZXNvbHV0aW9uCnBkZihwYXN0ZShmaWdfb3V0cywiVU1BUGxhYmVsbGVkOTAwMC5ub3Jhc3Rlci5wZGYiKSx3aWR0aCA9IDksIGhlaWdodCA9IDUpCiBEaW1QbG90KHNldSwgb3JkZXIgPSBjZWxsLnR5cGUub3JkZXIsIGNvbHMgPSBjbHVzdC5jb2xvdXJzLCBzaHVmZmxlID0gVFJVRSwgcmFzdGVyPUZBTFNFLHB0LnNpemUgPSAwLjEsIGxhYmVsID0gRkFMU0UpICsKICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNiksIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwgCiAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9MTYpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPTE2KSkKIGRldi5vZmYoKQoKRGltUGxvdChzZXUsIG9yZGVyID0gY2VsbC50eXBlLm9yZGVyLCBjb2xzID0gY2x1c3QuY29sb3Vycywgc2h1ZmZsZSA9IFRSVUUsIHJhc3Rlcj1GQUxTRSxwdC5zaXplID0gMC4xLCBsYWJlbCA9IFRSVUUpCgojIyMjIyMjIyBmaWd1cmUgM0UgaGVhdG1hcCBvZiB0aGUgCgojIHJlb3JkZXIgdGhlIGJhcnMgdG8gbWF0Y2ggdGhlIFVNQVAKbGV2ZWxzKHNldSkgPC0gYygiQXN0cm9jeXRlcyAxIiwgIkFzdHJvY3l0ZXMgMiIsIlJhZGlhbCBHbGlhIDEiLCJSYWRpYWwgR2xpYSAyIiwKICAgICAgICAgICAgICAgICAgICAgIlJhZGlhbCBHbGlhIDMiLCJHbGlhbC1saW5lYWdlIiwKICAgICAgICAgICAgICAgICAgICAgIkVwaXRoZWxpYWwiLCJFbmRvdGhlbGlhbCIsCiAgICAgICAgICAgICAgICAgICAgICJOUEMiLCJOZXVyb25zIDEiLCJOZXVyb25zIDIiLAogICAgICAgICAgICAgICAgICAgICAiT2xpZ29kZW5kcm9jeXRlcyIsIk9QQyIsIlN0ZW0gY2VsbCBsaWtlIiwiVW5hc3NpZ25lZCIpCgpwZGYocGFzdGUoZmlnX291dHMsIkhNOTAwMF9maWczRS5wZGYiKSx3aWR0aCA9IDguNSwgaGVpZ2h0ID0gNSkKRG9IZWF0bWFwKHNldSwgZmVhdHVyZXMgPSBBQiwgc2l6ZT0gNixzbG90ID0gInNjYWxlLmRhdGEiLCBncm91cC5jb2xvcnMgPSBjbHVzdC5jb2xvdXJzLCBkaXNwLm1heCA9IDIsIGRpc3AubWluID0gLTEuNSwKICAgICAgICAgIGFuZ2xlID0gOTApICsgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gYygiIzE1NGM3OSIsICIjZWVlZWU0IiwgIiNlMjg3NDMiKSkgKyAKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpkZXYub2ZmKCkKCmBgYAoKYGBge3J9CnNhdmVSRFMoc2V1LCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvTmF0TWV0aG9kSnVuZVN1Ym1pc3Npb24vU2V1OTAwMGxhYmxlc0p1bmUyMy5SRFMiKQoKc2V1IDwtcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzL05hdE1ldGhvZEp1bmVTdWJtaXNzaW9uL1NldTkwMDBsYWJsZXNKdW5lMjMuUkRTIikgCgpgYGAKClRyYWluIGEgUmFuZG9tIEZvcmVzdCBjbGFzc2lmaWVyCgpgYGB7cn0KCiMgZ2V0IHRoZSBuYW1lcyBvZiBodGUgbWV0YSBkYXRhIHRvIGtub3cgdGhlIGFubm90YXRpb25zIHRvIGNhbGwKY29sbmFtZXMoc2V1QG1ldGEuZGF0YSkKCkRpbVBsb3Qoc2V1LCBncm91cC5ieSA9ICJsYWJlbHMiKQoKYGBgCmBgYHtyfQoKbWFya2VycyA8LSByZXYoYygiQ0QyNCIsIkNENTYiLCJDRDI5IiwiQ0QxNSIsIkNEMTg0IiwiQ0QxMzMiLCJDRDcxIiwiQ0Q0NCIsIkdMQVNUIiwiQVFQNCIsIkhlcGFDQU0iLCAiQ0QxNDBhIiwiTzQiKSkKcmYgPC0gUkZNX3RyYWluKHNldXJhdGVfb2JqZWN0ID0gc2V1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBQl9saXN0ID0gbWFya2VycywgYW5ub3RhdGlvbnMgPSBzZXUkbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSBjKDAuNSwwLjUpLAogICAgICAgICAgICAgICAgICAgICAgZG93bnNhbXBsZSA9ICJub25lIiwKICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSAyMjIsCiAgICAgICAgICAgICAgICAgICAgICBteXRyeSA9IGMoMToxMCksCiAgICAgICAgICAgICAgICAgICAgICBtYXhub2RlcyA9IGMoMTA6IDIwKSwKICAgICAgICAgICAgICAgICAgICAgIHRyZWVzID0gYygyNTAsIDUwMCwgMTAwMCwyMDAwKSwKICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0X25vZGUgPSAxNSkKYGBgCgoK